/*
 * Wazuh Vulnerability Detection Module Configuration
 * Copyright (C) 2015, Wazuh Inc.
 * August 31, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef CLIENT
#ifndef WIN32
#include "wazuh_modules/wmodules.h"
#include "config/global-config.h"

static const char * valid_paths[] = {
    "enabled",
    "index-status",
    "feed-update-interval",
    "offline-url",
    "cti-url",
    NULL
};

/**
 * @brief Function used to read, recursively, the configuration related to vulnerability detection.
 *
 * @param old_vd If true, the old VD configuration will be read. In this case, the only config taken into account is 'enabled'.
 */
void wm_vulnerability_detection_subnode_read(const OS_XML *xml, XML_NODE nodes, cJSON *output_json, const bool old_vd)
{
    int i;
    xml_node **children;
    cJSON * subnode;
    cJSON * existing_item;
    cJSON * array_item;
    _Config global_config;
    char * subnode_keypath;
    size_t subnode_keypath_len;
    memset(&global_config, 0, sizeof(_Config));

    if (!nodes)
        return;

    // Iterate over elements
    for (i = 0; nodes[i]; i++) {
        subnode_keypath_len = strlen(nodes[i]->element) + 2;
        os_calloc(1, subnode_keypath_len, subnode_keypath);
        snprintf(subnode_keypath, subnode_keypath_len, "%s", nodes[i]->element);

        /* Unknown paths are ignored */
        if(!key_is_in_array(subnode_keypath, valid_paths)){
            mwarn(XML_INVELEM, subnode_keypath);
            free(subnode_keypath);
            continue;
        }
        free(subnode_keypath);

        const bool config_enabled = (strcmp(nodes[i]->element, "enabled") == 0);
        if (old_vd && !config_enabled)
        {
            // Old VD configs different from 'enabled' are ignored.
            continue;
        }

        if((children = OS_GetElementsbyNode(xml, nodes[i])))
        {
            subnode = cJSON_CreateObject();
            wm_vulnerability_detection_subnode_read(xml, children, subnode, old_vd);
            cJSON_AddItemToObject(output_json, nodes[i]->element, subnode);
            OS_ClearNode(children);
        }
        else
        {
            if((existing_item = cJSON_GetObjectItem(output_json, nodes[i]->element)))
            {
                // Already exists a key in JSON with this same name at this level
                if (old_vd)
                {
                    // Old VD config shouldn't overwrite any already present config.
                    continue;
                }
                else if(cJSON_IsArray(existing_item))
                {
                    // Item already is an array. Just add the new item to array
                    cJSON_AddItemToArray(existing_item, cJSON_CreateString(nodes[i]->content));
                }
                else
                {
                    // Overwrite the existing element.
                    cJSON_Delete(cJSON_DetachItemFromObject(output_json, nodes[i]->element));
                    cJSON_AddStringToObject(output_json, nodes[i]->element, nodes[i]->content);
                }
            }
            else
            {
                cJSON_AddStringToObject(output_json, nodes[i]->element, nodes[i]->content);
            }
        }
    }

    if (!cJSON_GetObjectItem(output_json, "cti-url"))
    {
        if (ReadConfig(CGLOBAL, OSSECCONF, &global_config, NULL) == 0) {
            cJSON_AddStringToObject(output_json, "cti-url", global_config.cti_url);
        }
    }
    config_free(&global_config);
}

int Read_Vulnerability_Detection(const OS_XML *xml, XML_NODE nodes, void *d1, const bool old_vd)
{
    wmodule **wmodules = (wmodule**)d1;
    wmodule *cur_wmodule = NULL;
    wmodule *cur_wmodule_exists;
    wm_vulnerability_scanner_t *vulnerability_scanner = NULL;

    // Allocate memory
    if (*wmodules) {
        cur_wmodule_exists = *wmodules;

        while (cur_wmodule_exists) {
            if(cur_wmodule_exists->tag) {
                if(strcmp(cur_wmodule_exists->tag, WM_VULNERABILITY_SCANNER_CONTEXT.name) == 0) {
                    cur_wmodule = cur_wmodule_exists;
                    break;
                }
            }

            if (cur_wmodule_exists->next == NULL) {
                os_calloc(1, sizeof(wmodule), cur_wmodule_exists->next);
                cur_wmodule = cur_wmodule_exists->next;
                cur_wmodule->context = &WM_VULNERABILITY_SCANNER_CONTEXT;
                cur_wmodule->tag = strdup(cur_wmodule->context->name);

                os_calloc(1, sizeof(wm_vulnerability_scanner_t), vulnerability_scanner);
                vulnerability_scanner->vulnerability_detection = cJSON_CreateObject();
                cur_wmodule->data = vulnerability_scanner;

                break;
            }

            cur_wmodule_exists = cur_wmodule_exists->next;
        }
    } else {
        os_calloc(1, sizeof(wmodule), cur_wmodule);
        *wmodules = cur_wmodule;
        cur_wmodule->context = &WM_VULNERABILITY_SCANNER_CONTEXT;
        cur_wmodule->tag = strdup(cur_wmodule->context->name);

        os_calloc(1, sizeof(wm_vulnerability_scanner_t), vulnerability_scanner);
        vulnerability_scanner->vulnerability_detection = cJSON_CreateObject();
        cur_wmodule->data = vulnerability_scanner;
    }

    if (!cur_wmodule) {
        merror(MEM_ERROR, errno, strerror(errno));
        return OS_INVALID;
    }

    if (!nodes) {
        mdebug1("Empty configuration for module '%s'", cur_wmodule->context->name);
        return OS_SUCCESS;
    }

    vulnerability_scanner = (wm_vulnerability_scanner_t *)cur_wmodule->data;

    wm_vulnerability_detection_subnode_read(xml, nodes, vulnerability_scanner->vulnerability_detection, old_vd);

    return OS_SUCCESS;
}

#endif
#endif
